import type { Directive } from 'vue';
import './index.less';
export interface RippleOptions {
   event: string;
   transition: number;
}

export interface RippleProto {
   background?: string;
   zIndex?: string;
}

export type EventType = Event & MouseEvent & TouchEvent;

const options: RippleOptions = {
   event: 'mousedown',
   transition: 400,
};

const RippleDirective: Directive & RippleProto = {
   beforeMount: (el: HTMLElement, binding) => {
      if (binding.value === false) return;

      const bg = el.getAttribute('ripple-background');
      setProps(Object.keys(binding.modifiers), options);

      const background = bg || RippleDirective.background;
      const zIndex = RippleDirective.zIndex;

      el.addEventListener(options.event, (event: EventType) => {
         rippler({
            event,
            el,
            background,
            zIndex,
         });
      });
   },
   updated(el, binding) {
      if (!binding.value) {
         el?.clearRipple?.();
         return;
      }
      const bg = el.getAttribute('ripple-background');
      el?.setBackground?.(bg);
   },
};

function rippler({
   event,
   el,
   zIndex,
   background,
}: { event: EventType; el: HTMLElement } & RippleProto) {
   const targetBorder = parseInt(getComputedStyle(el).borderWidth.replace('px', ''));
   const clientX = event.clientX || event.touches[0].clientX;
   const clientY = event.clientY || event.touches[0].clientY;

   const rect = el.getBoundingClientRect();
   const { left, top } = rect;
   const { offsetWidth: width, offsetHeight: height } = el;
   const { transition } = options;
   const dx = clientX - left;
   const dy = clientY - top;
   const maxX = Math.max(dx, width - dx);
   const maxY = Math.max(dy, height - dy);
   const style = window.getComputedStyle(el);
   const radius = Math.sqrt(maxX * maxX + maxY * maxY);
   const border = targetBorder > 0 ? targetBorder : 0;

   const ripple = document.createElement('div');
   const rippleContainer = document.createElement('div');

   // Styles for ripple
   ripple.className = 'ripple';

   Object.assign(ripple.style ?? {}, {
      marginTop: '0px',
      marginLeft: '0px',
      width: '1px',
      height: '1px',
      transition: `all ${transition}ms cubic-bezier(0.4, 0, 0.2, 1)`,
      borderRadius: '50%',
      pointerEvents: 'none',
      position: 'relative',
      zIndex: zIndex ?? '9999',
      backgroundColor: background ?? 'rgba(0, 0, 0, 0.12)',
   });

   // Styles for rippleContainer
   rippleContainer.className = 'ripple-container';
   Object.assign(rippleContainer.style ?? {}, {
      position: 'absolute',
      left: `${0 - border}px`,
      top: `${0 - border}px`,
      height: '0',
      width: '0',
      pointerEvents: 'none',
      overflow: 'hidden',
   });

   const storedTargetPosition =
      el.style.position.length > 0 ? el.style.position : getComputedStyle(el).position;

   if (storedTargetPosition !== 'relative') {
      el.style.position = 'relative';
   }

   rippleContainer.appendChild(ripple);
   el.appendChild(rippleContainer);

   Object.assign(ripple.style, {
      marginTop: `${dy}px`,
      marginLeft: `${dx}px`,
   });

   const {
      borderTopLeftRadius,
      borderTopRightRadius,
      borderBottomLeftRadius,
      borderBottomRightRadius,
   } = style;
   Object.assign(rippleContainer.style, {
      width: `${width}px`,
      height: `${height}px`,
      direction: 'ltr',
      borderTopLeftRadius,
      borderTopRightRadius,
      borderBottomLeftRadius,
      borderBottomRightRadius,
   });

   setTimeout(() => {
      const wh = `${radius * 2}px`;
      Object.assign(ripple.style ?? {}, {
         width: wh,
         height: wh,
         marginLeft: `${dx - radius}px`,
         marginTop: `${dy - radius}px`,
      });
   }, 0);

   function clearRipple() {
      setTimeout(() => {
         ripple.style.backgroundColor = 'rgba(0, 0, 0, 0)';
      }, 250);

      setTimeout(() => {
         rippleContainer?.parentNode?.removeChild(rippleContainer);
      }, 850);
      el.removeEventListener('mouseup', clearRipple, false);
      el.removeEventListener('mouseleave', clearRipple, false);
      el.removeEventListener('dragstart', clearRipple, false);
      setTimeout(() => {
         let clearPosition = true;
         for (let i = 0; i < el.childNodes.length; i++) {
            if ((el.childNodes[i] as Recordable).className === 'ripple-container') {
               clearPosition = false;
            }
         }

         if (clearPosition) {
            el.style.position = storedTargetPosition !== 'static' ? storedTargetPosition : '';
         }
      }, options.transition + 260);
   }

   if (event.type === 'mousedown') {
      el.addEventListener('mouseup', clearRipple, false);
      el.addEventListener('mouseleave', clearRipple, false);
      el.addEventListener('dragstart', clearRipple, false);
   } else {
      clearRipple();
   }

   (el as Recordable).setBackground = (bgColor: string) => {
      if (!bgColor) {
         return;
      }
      ripple.style.backgroundColor = bgColor;
   };
}

function setProps(modifiers: Recordable, props: Recordable) {
   modifiers.forEach((item: Recordable) => {
      if (isNaN(Number(item))) props.event = item;
      else props.transition = item;
   });
}

export default RippleDirective;
